/* Example of embedding Python in another program and calling python functions from C++*/

#include "python2.5/Python.h"
#include <pthread.h>
#include <unistd.h>

PyObject *pCallbackFunction, *pModule;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

static PyObject *set_callback(PyObject *self, PyObject* args, PyObject *keywds){
	char *function, *event, *key;
	static char *kwlist[] = {"function", "event", "key", NULL};
	if (!PyArg_ParseTupleAndKeywords(args, keywds, "ss|s", kwlist, &function, &event, &key))
		return NULL;
	pCallbackFunction = PyObject_GetAttrString(pModule, function);
	if (!pCallbackFunction || !PyCallable_Check(pCallbackFunction)){
		Py_XDECREF(pCallbackFunction);
		pCallbackFunction=NULL;
		printf("Failed to set callback\n");
		return Py_BuildValue("i", -1);
	}
	printf("Callback set\n");
	return Py_BuildValue("i",0);
};

//list of methods for module "embedder"
static PyMethodDef embedder_methods[] = {
	{"setCallback", (PyCFunction)set_callback, METH_KEYWORDS, "Set a Python function as callback from C++."},
	{NULL, NULL}
};

//function to initialize the "embedder" module
void initembedder(){
	PyImport_AddModule("embedder");
	Py_InitModule("embedder", embedder_methods);
};

//run function hidden in pCallbackFunction
void* run_callback(void* arg){
	PyObject *pArgs, *pValue;
	int *thread_num=(int*)arg;
	int j;
	/*printf("LOCK");
	PyEval_AcquireLock();
	printf("!");*/
	if (pCallbackFunction && PyCallable_Check(pCallbackFunction)) {
		pArgs = PyTuple_New(0);
		for (j=0; j<200; j++){
			pthread_mutex_lock (&mutex);
			pValue = PyObject_CallObject(pCallbackFunction, pArgs);
			printf("%i ", *thread_num);
			pthread_mutex_unlock (&mutex);
		}
		Py_DECREF(pArgs);
		if (pValue != NULL) {
			Py_DECREF(pValue);
		}
		else {
			fprintf(stderr,"Callback failed\n");
			PyErr_Print();
		}
	}
	/*printf("UNLOCK");
	PyEval_ReleaseLock();
	printf("!");*/
	return NULL;
};

main(int argc, char **argv){
	Py_SetProgramName(argv[0]);
	Py_Initialize();
	initembedder();
	PySys_SetArgv(argc, argv);
	//PyEval_InitThreads();
	PyRun_SimpleString("print \"Program start\"\n");
	PyObject *pName, *pFunc;
	PyObject *pArgs, *pValue;
	int i;
	pName = PyString_FromString("embed");
	/* Error checking of pName left out */
	pModule = PyImport_Import(pName);
	Py_DECREF(pName);
	if (pModule != NULL) {
		pFunc = PyObject_GetAttrString(pModule, "initialize");
		if (pFunc && PyCallable_Check(pFunc)) {
			pArgs = PyTuple_New(0);
			//PyTuple_SetItem(pArgs, 0, PyInt_FromLong(8));
			pValue = PyObject_CallObject(pFunc, pArgs);
			Py_DECREF(pArgs);
			if (pValue != NULL) {
				printf("Result of call: %ld\n", PyInt_AsLong(pValue));
				Py_DECREF(pValue);
			}
			else {
				Py_DECREF(pFunc);
				Py_DECREF(pModule);
				PyErr_Print();
				fprintf(stderr,"Call failed\n");
			}
		}
		else {
			if (PyErr_Occurred())
				PyErr_Print();
			fprintf(stderr, "Cannot find function \"initialize\"\n");
		}
		Py_XDECREF(pFunc);
		Py_DECREF(pModule);
	}
	else {
		PyErr_Print();
		fprintf(stderr, "Failed to load \"embed.py\"\n");
	}
	printf("going to start threads");
	//try to run the function run_callback() in two threads
	const int thread_count=100;
	pthread_t thread[thread_count];
	int thread_numbers[thread_count];
	void** result;
	printf("UNLOCK!");
	//PyEval_ReleaseLock();
	for (i=0; i<thread_count; i++){
		thread_numbers[i]=i;
		fprintf(stdout,"*%i ",i);
		pthread_create(&thread[i], NULL, &run_callback, (void*)&thread_numbers[i]);
	}
	printf("started all.\n");
	for (i=0; i<thread_count; i++){
		fprintf(stdout,"#%i ",i);
		pthread_join(thread[i], NULL);
	}
	/* Note that you can call any public function of the Python
	   interpreter here, e.g. call_object(). */
	Py_Finalize();
	printf("Goodbye, cruel world\n");
	return 0;
};

/*
Program start
Callback set
Initialized
Result of call: 1
going to start threadsUNLOCK!*0*1LOCK!LOCK!*2LOCK!*3LOCK!*4LOCK!*5LOCK!*6LOCK!*7LOCK!*8LOCK!*9LOCK!*10LOCK!*11LOCK!*12LOCK!*13LOCK!*14LOCK!*15LOCK!*16LOCK!*17LOCK!*18LOCK!*19LOCK!*20LOCK!*21LOCK!*22LOCK!*23LOCK!*24LOCK!*25LOCK!*26LOCK!*27LOCK!*28LOCK!*29LOCK!*30LOCK!*31LOCK!*32LOCK!*33LOCK!*34LOCK!*35LOCK!*36LOCK!*37LOCK!*38LOCK!*39LOCK!*40LOCK!*41LOCK!*42LOCK!*43LOCK!*44LOCK!*45LOCK!*46LOCK!*47LOCK!*48LOCK!*49LOCK!*50LOCK!*51LOCK!*52LOCK!*53LOCK!*54LOCK!*55LOCK!*56LOCK!*57LOCK!*58LOCK!*59LOCK!*60LOCK!*61LOCK!*62LOCK!*63LOCK!*64LOCK!*65LOCK!*66LOCK!*67LOCK!*68LOCK!*69LOCK!*70LOCK!*71LOCK!*72LOCK!*73LOCK!*74LOCK!*75LOCK!*76LOCK!*77LOCK!*78LOCK!*79LOCK!*80LOCK!*81LOCK!*82LOCK!*83LOCK!*84LOCK!*85LOCK!*86LOCK!*87LOCK!*88LOCK!*89LOCK!*90LOCK!*91LOCK!*92LOCK!*93LOCK!*94LOCK!*95LOCK!*96LOCK!*97LOCK!*98LOCK!*99LOCK!started all.
Segmentation fault
*/
